package com.easyapi.service.execute.impl;


import com.alibaba.druid.pool.DruidDataSource;
import com.easyapi.common.Constants;
import com.easyapi.common.bean.ApiCenterDatasource;
import com.easyapi.common.bean.ApiCenterInterfaceInfo;
import com.easyapi.common.emun.StatusCode;
import com.easyapi.common.exception.BizException;
import com.easyapi.common.param.api.OutputParam;
import com.easyapi.common.utils.BaseResponse;
import com.easyapi.common.utils.ResultUtils;
import com.easyapi.config.PoolConfig;
import com.easyapi.mapper.ApiCenterDatasourceMapper;
import com.easyapi.mapper.ApiCenterInterfaceInfoMapper;
import com.easyapi.service.execute.GenerateApiExecuteService;
import com.easyapi.service.sync.AsyncMethodHandler;
import com.easyapi.utils.CaffeineCacheUtil;
import com.easyapi.utils.GenerateSqlUtil;
import com.easyapi.utils.RedisCache;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.collections4.MapUtils;
import org.springframework.boot.configurationprocessor.json.JSONArray;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * @author javadadi
 * 参数配置
 */
@Service
public class GenerateApiExecuteServiceImpl implements GenerateApiExecuteService {
    @Resource
    private ApiCenterDatasourceMapper datasourceMapper;

    @Resource
    private ApiCenterInterfaceInfoMapper interfaceInfoMapper;

    @Resource
    private RedisCache redisCache;

    @Override
    public BaseResponse<Object> execute(String path, Map<String, String> paramMap) throws ExecutionException, InterruptedException {
        ApiCenterInterfaceInfo interfaceInfo = getInterfaceInfo(path);
        // 向导模式现在只支持select
        Long datasourceId = interfaceInfo.getDatasourceId();
        ApiCenterDatasource datasource = datasourceMapper.selectById(datasourceId);
        if (Objects.isNull(datasource)) {
            throw new BizException(StatusCode.NO_SUCH_DATASOURCE);
        }
        String dataName = datasource.getDataName();
        DruidDataSource druidDataSource = (DruidDataSource) CaffeineCacheUtil.get(dataName + "_" + datasource.getId());
        if (Objects.isNull(druidDataSource)) {
            return ResultUtils.success(null);
        }

        // 使用线程池执行数据库操作
        Future<JSONArray> futureResult = PoolConfig.RUN_SYNC_JOB_POOL.submit(() -> {
            JSONArray resultsVos = new JSONArray();
            //try 语句结束的时候自动释放链接
            try (Connection connection = druidDataSource.getConnection();
                 PreparedStatement preparedStatement = prepareStatement(connection, interfaceInfo, paramMap);
                 ResultSet resultSet = preparedStatement.executeQuery()) {
                // 输出参数处理
                Type outputType = new TypeToken<List<OutputParam>>() {
                }.getType();
                String outputParamsJson = interfaceInfo.getOutputParams();
                List<OutputParam> outputParams = new Gson().fromJson(outputParamsJson, outputType);
                if (resultSet.next()) {
                    for (OutputParam outputParam : outputParams) {
                        String value = resultSet.getString(outputParam.getBindField());
                        JSONObject vo = new JSONObject();
                        vo.put(outputParam.getParamName(), value);
                        resultsVos.put(vo);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return resultsVos;
        });
        JSONArray resultsVos;
        try {
            resultsVos = futureResult.get(); // 等待任务完成并获取结果
        } catch (ExecutionException | InterruptedException e) {
            throw new RuntimeException("Error executing SQL query", e);
        }

        return ResultUtils.success(resultsVos);
    }

    private PreparedStatement prepareStatement(Connection connection, ApiCenterInterfaceInfo interfaceInfo, Map<String, String> paramMap) throws SQLException {
        String sql = GenerateSqlUtil.selectSql(interfaceInfo, paramMap);
        return connection.prepareStatement(sql);
    }


    @SuppressWarnings("all")
    private ApiCenterInterfaceInfo getInterfaceInfo(String path) {
        Map<String, ApiCenterInterfaceInfo> apiMap = CaffeineCacheUtil.get(Constants.INTERFACE_PATH_MAP, Map.class);
        if (MapUtils.isNotEmpty(apiMap)) {
            if (apiMap.containsKey(path)) {
                return apiMap.get(path);
            }
        } else if (redisCache.hasKey(Constants.INTERFACE_PATH_MAP)) {
            Map<String, ApiCenterInterfaceInfo> pathMap = (Map<String, ApiCenterInterfaceInfo>) redisCache.get(Constants.INTERFACE_PATH_MAP);
            if (path.contains(path)) {
                return pathMap.get(path);
            }
        }
        ApiCenterInterfaceInfo interfaceInfo = interfaceInfoMapper.getByApiPath(path);
        if (Objects.isNull(interfaceInfo)) {
            throw new BizException(StatusCode.NO_SUCH_API);
        }
        return interfaceInfo;
    }

}
